// cc: cocos creater;
// toutiao ---> tt wx
// _decorator：注解集合
// Component：组件类的基类
// Node：节点类型；
import {
  _decorator,
  Component,
  Node,
  Vec3,
  Mat4,
  Camera,
  RigidBody,
  v3,
  systemEvent,
  SystemEvent,
  tween,
  instantiate,
  // NodePool,
  Quat,
  Label,
} from "cc";

import { NodePool } from "cc";
// ccclass：描述我们一个组件类
// property：描述数据成员 ---> 编辑器
const { ccclass, property } = _decorator;

enum State {
  Init,
  Idle,
  Group,
  PassBridge,
}

@ccclass("GameMgr")
export class GameMgr extends Component {
  @property(Node)
  private player: Node = null;

  @property(Node)
  private curPlat: Node = null;

  @property(Node)
  private nextPlat: Node = null;

  @property(Node)
  private bridge: Node = null;

  @property(Node)
  private Camera: Node = null;

  @property(Node)
  private platRoot: Node = null;

  @property(Node)
  private playNode: Node = null;

  @property(Node)
  private scoreNode: Node = null;

  @property(Node)
  private timeNode: Node = null;

  private isGoToLeft: boolean = false;

  private state: State = State.Init;

  private brigeOffset: number = 0.009 * 1.414;

  private groupSpeed: number = 0.05;
  private brigeLen: number = 0;
  private moveSpeed: number = 0.05;

  private nodeList: Node[] = [];

  // 实例化 NodePool
  private nodePool: NodePool = new NodePool();

  private score: number = 0;
  private time: number = 0;

  onLoad(): void {
    // 组件实例加载的时候调用
  }

  start(): void {
    // 开始运行之前调用 --> 初始入口
    // console.log("start");
    // 将场景中原始的节点放入动态节点管理
    this.nodeList.push(this.curPlat);
    this.nodeList.push(this.nextPlat);
    // 初始化节点池

    let nextPos = this.nextPlat.getWorldPosition();
    nextPos.x -= 0.05;
    this.Camera.lookAt(nextPos);

    // 玩家的位置
    this.isGoToLeft = false;
    let pos = this.curPlat.getWorldPosition();
    this.player.setWorldPosition(pos);
    this.playerLookAt();
    // end

    // 对我们桥的位置
    this.bridgePos(pos);
    // end
  }

  // 玩家朝向
  playerLookAt() {
    this.player.lookAt(this.nextPlat.worldPosition);
    let e_rot: Vec3 = this.player.eulerAngles;
    e_rot.y += 180;
    this.player.eulerAngles = e_rot;
  }

  bridgePos(pos) {
    // 对我们桥的位置
    if (this.isGoToLeft) {
      pos.x -= 0.009;
    } else {
      pos.x += 0.009;
    }
    pos.z -= 0.009;
    this.bridge.setWorldPosition(pos);
    this.bridge.lookAt(this.nextPlat.worldPosition);
    let e_rot: Vec3 = this.player.eulerAngles;
    e_rot.x += 90;
    this.bridge.eulerAngles = e_rot;
    // 设置桥的长度
    this.setBridgeLength(0);
    // end
  }

  onTouchStart(e): void {
    if (this.state !== State.Idle) return;
    this.state = State.Group;
    this.brigeLen = 0;
  }

  onTouchEnd(e): void {
    if (this.state !== State.Group) return;
    this.state = State.PassBridge;

    // 播放一个动画，桥倒下去；  ---> tween;
    let e_rot = this.bridge.eulerAngles.clone();
    e_rot.x += 90;

    tween(this.bridge)
      .to(0.5, { eulerAngles: e_rot })
      .call(() => {
        this.passBridge();
      })
      .start();
  }

  private checkSuccess(): boolean {
    let pos = v3();
    Vec3.subtract(
      pos,
      this.bridge.getWorldPosition(),
      this.nextPlat.getWorldPosition()
    );
    let minLen = Vec3.len(pos) - 0.015 * this.nextPlat.getScale().x;
    let maxLen = Vec3.len(pos) + 0.015 * this.nextPlat.getScale().x;
    // 条件判断
    // 判断木桥的长度
    return this.brigeLen > minLen && this.brigeLen < maxLen;
  }

  private passBridge(): void {
    if (this.checkSuccess()) {
      this.passBridgeSuccess();
    } else {
      this.passBridgeFailed();
    }
  }

  private passBridgeSuccess(): void {
    let dst = this.nextPlat.getWorldPosition();
    let src = this.curPlat.getWorldPosition();
    let len = Vec3.distance(dst, src);
    // 计算偏移距离
    let sub: Vec3 = v3();
    Vec3.subtract(sub, dst, src);
    // 获取摄像机的世界坐标
    let cameraWorldPos: Vec3 = this.Camera.getWorldPosition();
    let tarCameraWorldPos: Vec3 = v3();
    Vec3.add(tarCameraWorldPos, cameraWorldPos, sub);
    // 计算时间 cur ---> next 距离 /速度;
    let time = len / this.moveSpeed;
    // 玩家移动
    tween(this.player)
      .to(time, { worldPosition: dst })
      .call(() => {
        this.score++;
        this.scoreNode.getComponent(Label).string = "分数：" + this.score;
        this.genNewPlat();
      })
      .start();
    // 摄像机跟拍玩家
    tween(this.Camera)
      .to(time, {
        worldPosition: tarCameraWorldPos,
      })
      .start();
  }

  private genNewPlat(): void {
    // 创建一个桥墩
    // 从节点池拿一个节点，节点池没有就新实例化一个
    let newPlat = this.nodePool.get() || instantiate(this.nextPlat);
    if (this.nodeList.length <= 4) {
      this.nodeList.push(newPlat);
    } else {
      // 同步数据
      this.nodePool.put(this.nodeList[0]);
      this.nodeList.splice(0, 1);
    }
    let scaleNum = 1 + Math.random() * 0.5;
    newPlat.setScale(v3(scaleNum, 1, scaleNum));
    this.platRoot.addChild(newPlat);

    let len = 0.05 + Math.random() * 0.05;
    let pos = this.nextPlat.getWorldPosition();
    if (this.isGoToLeft === false) {
      // 放左边
      pos.x -= len;
    } else {
      // 右边
      pos.x += len;
    }
    pos.z -= len;
    newPlat.setWorldPosition(pos);

    // 摄像机器操作
    // 1、计算偏移向量
    // let movePos = v3();
    // Vec3.subtract(
    //   movePos,
    //   this.nextPlat.getWorldPosition(),
    //   this.curPlat.getWorldPosition()
    // );

    // 2、移动摄像机器位置
    // let cameraPos = this.Camera.getWorldPosition();
    // cameraPos.x += movePos.x;
    // cameraPos.y += movePos.y;
    // cameraPos.z += movePos.z;
    // this.Camera.setWorldPosition(cameraPos);

    // 3、移动摄像机角度

    // let nextPos = newPlat.getWorldPosition();
    // if (this.isGoToLeft) {
    //   nextPos.x -= 0.05;
    // } else {
    //   nextPos.x += 0.05;
    // }
    // this.Camera.lookAt(nextPos);

    this.curPlat = this.nextPlat;
    this.nextPlat = newPlat;
    this.isGoToLeft = !this.isGoToLeft;

    this.state = State.Idle;

    this.playerLookAt();
    // 对我们桥的位置
    this.bridgePos(this.curPlat.getWorldPosition());
  }

  private passBridgeFailed(): void {
    clearTimeout(this.timer.prototype.timerFun);
    this.state = State.Init;
    systemEvent.off(SystemEvent.EventType.TOUCH_START, this.onTouchStart, this);
    systemEvent.off(SystemEvent.EventType.TOUCH_END, this.onTouchEnd, this);
  }

  private setBridgeLength(len: number) {
    let cube = this.bridge.getChildByName("Cube");
    cube.active = len <= 0 ? false : true;
    cube.setScale(v3(0.02, 0.0001, len));
    cube.setPosition(v3(0, 0, -len * 0.5));
  }

  // ｜-----｜-----｜------｜----- dt ----|
  // 100 * dt ---> 100 * dt;
  update(dt: number): void {
    // 每次画面刷新的时候调用
    // console.log("update");
    switch (this.state) {
      case State.Init:
        this.playNode.active = true;
        break;
      case State.Group:
        this.brigeLen += this.groupSpeed * dt;
        this.setBridgeLength(this.brigeLen);
        break;
    }
  }

  private startGame() {
    this.state = State.Idle;
    this.playNode.active = false;
    this.time = 0;
    this.timer();
    this.score = 0;
    this.scoreNode.getComponent(Label).string = "分数：" + this.score;
    this.brigeLen = 0;
    this.setBridgeLength(this.brigeLen);
    let e_rot = this.bridge.eulerAngles.clone();
    e_rot.x = 90;
    this.bridge.eulerAngles = e_rot;

    // 监听触摸消息
    systemEvent.on(SystemEvent.EventType.TOUCH_START, this.onTouchStart, this);
    systemEvent.on(SystemEvent.EventType.TOUCH_END, this.onTouchEnd, this);
  }

  // 记时器
  timer() {
    this.timer.prototype.timerFun = setTimeout(() => {
      this.time++;
      this.timer();
    }, 1000);
    // 更新时间
    // 时间模式
    this.timeNode.getComponent(Label).string =
      "时间：" + this.s_to_hs(this.time);
  }
  // 将秒转换为 分:秒
  s_to_hs(s: any) {
    //计算分钟
    //算法：将秒数除以60，然后下舍入，既得到分钟数
    let h: any = Math.floor(s / 60);
    //计算秒
    //算法：取得秒%60的余数，既得到秒数
    s = s % 60;
    //将变量转换为字符串
    s = s.toString();
    h = h.toString();
    //如果只有一位数，前面增加一个0
    h = h.length == 1 ? "0" + h : h;
    s = s.length == 1 ? "0" + s : s;
    return h + " : " + s;
  }
}
